#include "Token.h"
#include <string>
#include <stdlib.h>
#include <iostream>
#include <sstream>

using namespace std;

int lineNo = 0;

/* since enumerations are essentially just numbers, we can use their names
   as data elements for an array */
string tokenList[] = {"//", "{", "}", "<", ">", "(", ")", ",", "camera",
    "light_source", "sphere", "plane", "box", "triangle", "cone",
    "pigment", "color", "rgb", "rgbf", "finish", "translate", "rotate",
    "scale", "ambient", "diffuse", "specular", "roughness", "reflection",
    "refraction", "ior", "light_finish", "location", "up", "right", "look_at",
    "bump_map", 
    "EOF"};

Token lookUpToken(string s) {
    int i = 0;
    while (i < TOKEN_NUM) /*tokenList[i].compare("") != 0)*/ {
        if (s.compare(tokenList[i]) == 0) {
            return (Token) i;
        }

        i++;
    }

    if (strtol(s.c_str(), NULL, 10) == 0) {
        return Eof;
    }
    cout << "Unknown token: \"" << s << "\" @line: " << lineNo << endl;
    exit(1);
}

string lookUpName(Token t) {
    return tokenList[t];
}

Symbol nextToken(ifstream &file) {
    char c;
    string build = "";
    Symbol ret;

    file >> c;
    while (isspace(c) && file.good()) {
        if (c == '\n') {
            lineNo++;
        }
        file >> c;
    }

    build.push_back(c);
    if (isalpha(c)) {
        c = file.peek();
        while(isalpha(c) || c == '_') {
            file >> c;
            build.push_back(c);
            c = file.peek();
        }

        ret.isToken = true;
        ret.t = lookUpToken(build);

        return ret;
    }
    else if (c == '/') {
        file >> c;
        if (c == '/') {
            ret.isToken = true;
            string line;
            getline(file, line);

            lineNo++;
            return nextToken(file);
        }
        else if (c == '*') {
            file >> c;
            while (file.good()) {
                if (c == '\n')
                    lineNo++;
                if (c == '*') {
                    file >> c;
                    if (c == '/') {
                        return nextToken(file);
                    }
                }

                file >> c;
            }
            ret.t = Eof;
            ret.isToken = true;
            return ret;
        }
        
        cout << "incorrect format: found /" << c << " expected // @line: "
            << lineNo << "\n";
        exit(1);
    }
    else if (isdigit(c) || c == '.' || c == '-') {
        bool foundDot = (c == '.');
        bool isNeg = (c == '-');

        c = file.peek();
        while (isdigit(c) || c == '.') {
            file >> c;
            build.push_back(c);
            if (c == '.') {
                if (foundDot) {
                    cout << "Incorrect number format @line: " << 
                        lineNo << endl;
                    exit(1);
                }

                foundDot = true;
            }

            c = file.peek();
        }

        ret.isToken = false;
        istringstream(build) >> ret.num;

        return ret;
    }
    else {
        ret.isToken = true;
        ret.t = lookUpToken(build);

        return ret;
    }
}

Symbol matchToken(ifstream &file, Symbol cur, Token match) {
    if (cur.isToken && cur.t == match) {
        return nextToken(file);
    }
    
    cout << "expected '" << lookUpName(match) << "' found '" <<
        lookUpName(cur.t) << "' @line: " << lineNo << endl;
    exit(1);
}

